package org.ala.tiktools.sharddb.mybatis.double_write;


import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;

import org.ala.tiktools.logs.LoggerFactory;
import org.ala.tiktools.mybatis.spring.TikToolsMyBatisSpringContext;
import org.ala.tiktools.sharddb.exception.ShardDBRuntimeException;
import org.ala.tiktools.sharddb.mybatis.double_write.impls.DefaultDoubleWriter;
import org.ala.tiktools.sharddb.mybatis.executor.ShardExecutor;
import org.ala.tiktools.sharddb.mybatis.executor.ShardTxExecutor;
import org.ala.tiktools.sharddb.mybatis.interceptor.Shard;
import org.ala.tiktools.sharddb.mybatis.router.IShardRouter;
import org.ala.tiktools.sharddb.mybatis.router.ShardInfo;
import org.ala.tiktools.sharddb.mybatis.router.ShardRouterHandler;
import org.ala.tiktools.sharddb.mybatis.transaction.ShardTransaction;
import org.ala.tiktools.tools.CachingKVTools;
import org.slf4j.Logger;

/**
 * 双写执行器
 *
 * @author ala
 * @date 2025-02-27 12:19
 */
public class DoubleWriterHandler {


    static Logger log = LoggerFactory.getLogger("shard", "double_writer");


    /**
     *  路由器
     */
    protected ShardRouterHandler routerHandler = new ShardRouterHandler();
    /**
     *  事务执行器
     */
    protected ShardTxExecutor txExecutor = new ShardTxExecutor();
    /**
     *  默认的双写实现
     */
    protected IDoubleWriter defaultDoubleWriter = new DefaultDoubleWriter();


    /**
     *  执行双写
     *  @param  threadPool 执行双写的线程池
     */
    public void doDoubleWrite(ThreadPoolExecutor threadPool, Object proxy, Method method, Object[] args, Map<ShardInfo, Object[]> oldShards, Map<ShardInfo, ShardExecutor.Result> results) {
        doDoubleWriteWithThread(threadPool, proxy, method, args, oldShards, results);
    }
    protected void doDoubleWriteWithThread(ThreadPoolExecutor threadPool, Object proxy, Method method, Object[] args, Map<ShardInfo, Object[]> oldShards, Map<ShardInfo, ShardExecutor.Result> results) {
        threadPool.execute(() -> {
            //  如果没有配双写，不予执行
            ShardDoubleWriter doubleWriterAnno = shardDoubleWriterMapper.get(method);
            if (doubleWriterAnno == null) {return;}

            //  拿新路由器
            IShardRouter router = router(doubleWriterAnno);
            if (router == null) {return;}

            //  重新过一遍路由，如果发现路由结果和原路由结果不一致，则重新执行一遍事务
            Shard anno = method.getAnnotation(Shard.class);
            Map<ShardInfo, Object[]> newShards = routerHandler.route(router, proxy, method, args);
            if (newShards == null || newShards.isEmpty()) {return;}

            //  对新路由再执行一遍事务操作
            ShardTransaction txAnno = method.getAnnotation(ShardTransaction.class);
            doDoubleWrite(doubleWriterAnno, proxy, method, newShards, results);
        });
    }


    /**
     *  执行双写
     */
    protected void doDoubleWrite(ShardDoubleWriter doubleWriterAnno, Object proxy, Method method, Map<ShardInfo, Object[]> args, Map<ShardInfo, ShardExecutor.Result> results) {
        //  拿IDoubleWriter实现
        IDoubleWriter writer = defaultDoubleWriter;
        if (doubleWriterAnno.execute() != null && doubleWriterAnno.execute().length > 0) {
            writer = doubleWriter(doubleWriterAnno);
        }
        writer.invoke(proxy, method, args, results);
    }


    protected ShardDoubleWriter NULL = new ShardDoubleWriter(){
        public Class<? extends IShardRouter> newRouter() {
            return null;
        }
        public Class<? extends IDoubleWriter>[] execute() {
            return null;
        }
        public Class<? extends Annotation> annotationType() {
            return null;
        }
    };
    /**
     *  method和@ShardDoubleWriter注解的对应关系
     */
    protected CachingKVTools<Method, ShardDoubleWriter> shardDoubleWriterMapper = new CachingKVTools<>(NULL, m -> {
        return m.getAnnotation(ShardDoubleWriter.class);
    });
    /**
     *  method和IShardRouter对应
     */
    protected IShardRouter router(ShardDoubleWriter anno) {
        try {
            return TikToolsMyBatisSpringContext.getBeanOrNew(anno.newRouter());
        } catch (InstantiationException | IllegalAccessException e) {
            throw new ShardDBRuntimeException(String.format("[DoubleWriterHandler router] 拿不到类型为%s的Bean，也无法new出来", anno.newRouter().getName()), e);
        }
    }
    /**
     *  双写执行器
     */
    protected IDoubleWriter doubleWriter(ShardDoubleWriter anno) {
        try {
            return TikToolsMyBatisSpringContext.getBeanOrNew(anno.execute()[0]);
        } catch (InstantiationException | IllegalAccessException e) {
            throw new ShardDBRuntimeException(String.format("[DoubleWriterHandler doubleWriter] 拿不到类型为%s的Bean，也无法new出来", anno.execute()[0].getName()), e);
        }
    }
}
